package com.orm.executor;

import com.orm.annotation.NotField;
import com.orm.annotation.TableFiled;
import com.orm.handler.TypeHandler;
import com.orm.handler.TypeHandlerRegister;
import com.orm.mapping.MapperStatement;
import com.orm.mapping.MappingProcess;
import com.orm.mapping.xmltag.DynamicContext;
import com.orm.mapping.xmltag.MixedSqlNode;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;

/**
 * 执行器，真正执行 sql 代码的地方
 *
 * 执行器没必要每个mapper 存一份引用， 每个session 持有引用即可 ，多个 mapper 共用一个执行器
 *
 *
 *
 *
 * sql 处理流程，从 mapperStatement 中取到 原始的未经解析的 sql   rawSql
 *
 * 如果没有任何  #  { }  < >  则视为普通 sql,不需要参数
 */
@Slf4j
public class DefaultExecutor implements Executor {
	protected DataSource dataSource;
	protected TypeHandlerRegister register = TypeHandlerRegister.newInstance();
	
	
	public DefaultExecutor(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	@Override
	public Connection getConnection() throws SQLException {
		return dataSource.getConnection();
	}
	
	// 关闭资源,默认对于连接就是直接关闭， 子类 SimpleExecutor 从连接池获得资源，是不关闭连接
	@Override
	public void close(Connection conn, PreparedStatement ps, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException exception) {
				// ignore
			}
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (SQLException exception) {
				// ignore
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException exception) {
				// ignore
			}
		}
	}
	
	
	/**
	 * 用来查 某个条件下的符合要求的记录数量，只要求数量， select count(1) 这里
	 *
	 * @param mapperStatement
	 * @param args
	 * @return
	 */
	@Override
	public int selectCount(MapperStatement mapperStatement, Object[] args) {
		// ######## 通用部分开始 #############
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		int returnValue = 0;   // 影响行数，或者返回主键，或返回查询到的条数
		String finalSql = "";
		List<Object> argList = new ArrayList<>();
		
		finalSql = prepareSql(mapperStatement, args, argList);
		
		try {
			conn = getConnection();
			ps = conn.prepareStatement(finalSql);   // 要返回主键的，这里改一下
			setParameters(ps, argList);
			// ######## 通用部分结束 #############
			
			rs = ps.executeQuery();    // 核心工作
			rs.next();
			returnValue = rs.getInt(1);
			return returnValue;
		} catch (SQLException exception) {
			log.error("出错SQL语句: {} ", finalSql);
			log.error("出错方法: {} ", mapperStatement.getId());
			log.error(exception.getMessage());
			throw new RuntimeException("SQL异常");
		} finally {
			close(conn, ps, rs);
		}
	}
	
	/**
	 * 返回单个实体 ，根据 returnType 来创建对象
	 *
	 * @param mapperStatement
	 * @param args
	 */
	@Override
	public Object selectOne(MapperStatement mapperStatement, Object[] args) {
		List<Object> resultList = selectList(mapperStatement, args);
		
		if (resultList.isEmpty()) {
			return null;
		}
		if (resultList.size() == 1) {
			return resultList.get(0);
		} else {
			throw new RuntimeException("结果数量太多,不能用selectOne方法");
		}
	}
	
	/**
	 * 返回多个实体，组装成List 返回
	 *
	 * @param mapperStatement
	 * @param args
	 */
	@Override
	public List<Object> selectList(MapperStatement mapperStatement, Object[] args) {
		// String returnType = mapperStatement.getReturnType();
		// String elementType = ClassUtil.getReturnTypeString(returnType);  //  不应该这样获取
		Class<?> clazz = mapperStatement.getReturnType();
		
		// ######## 通用部分开始 #############
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		int returnValue = 0;   // 影响行数，或者返回主键，或返回查询到的条数
		String finalSql = "";
		List<Object> argList = new ArrayList<>();
		
		finalSql = prepareSql(mapperStatement, args, argList);
		
		try {
			conn = getConnection();
			ps = conn.prepareStatement(finalSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);   // 要返回主键的，这里改一下
			setParameters(ps, argList);
			// ######## 通用部分结束 #############
			
			
			rs = ps.executeQuery();    // 核心工作
			
			List<?> objects = parseResultSet(rs, clazz);
			
			return (List<Object>) objects;
		} catch (SQLException exception) {
			log.error("出错SQL语句: {} ", finalSql);
			log.error("出错方法: {} ", mapperStatement.getId());
			log.error(exception.getMessage());
			throw new RuntimeException("SQL异常");
		} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
			log.error("实例化异常: {} ", clazz.getName());
			throw new RuntimeException("实例化异常");
		} finally {
			close(conn, ps, rs);
		}
	}
	
	
	// insert 独立出来，返回插入的主键
	@Override
	public int insert(MapperStatement mapperStatement, Object[] args) {
		// ######## 通用部分开始 #############
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		int returnValue = 0;   // 影响行数，或者返回主键，或返回查询到的条数
		String finalSql = "";
		List<Object> argList = new ArrayList<>();
		
		finalSql = prepareSql(mapperStatement, args, argList);
		
		try {
			conn = getConnection();
			ps = conn.prepareStatement(finalSql, Statement.RETURN_GENERATED_KEYS);    // 要返回主键的，这里改一下
			setParameters(ps, argList);
			// ######## 通用部分结束 #############
			
			
			ps.executeUpdate();    // 核心工作
			
			rs = ps.getGeneratedKeys();
			if (rs.next()) {
				Long keyId = rs.getLong(1);
				return keyId.intValue();
			}
			return 0;
		} catch (SQLException exception) {
			log.error("出错SQL语句: {} ", finalSql);
			log.error("出错方法: {} ", mapperStatement.getId());
			log.error(exception.getMessage());
			throw new RuntimeException("SQL异常");
		} finally {
			close(conn, ps, rs);
		}
	}
	
	@Override
	public int update(MapperStatement mapperStatement, Object[] args) {
		// ######## 通用部分开始 #############
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		int returnValue = 0;   // 影响行数，或者返回主键，或返回查询到的条数
		String finalSql = "";
		List<Object> argList = new ArrayList<>();
		
		finalSql = prepareSql(mapperStatement, args, argList);
		
		try {
			conn = getConnection();
			ps = conn.prepareStatement(finalSql, Statement.RETURN_GENERATED_KEYS);    // 要返回主键的，这里改一下
			setParameters(ps, argList);
			// ######## 通用部分结束 #############
			
			returnValue = ps.executeUpdate();    // 核心工作
			return returnValue;
		} catch (SQLException exception) {
			log.error("出错SQL语句: {} ", finalSql);
			log.error("出错方法: {} ", mapperStatement.getId());
			log.error(exception.getMessage());
			throw new RuntimeException("SQL异常");
		} finally {
			close(conn, ps, rs);
		}
	}
	
	/**
	 * 处理参数名字到实际值的转换映射       参数名 ->  实际参数object 的映射
	 *
	 * 所有sql语句都需要处理
	 */
	public Map<String, Object> mappingParamNameToValue(MapperStatement mapperStatement, Object[] args) {
		if (args == null || args.length == 0) {
			return null;
		}
		
		SortedMap<Integer, String> paramIndexToName = mapperStatement.getParamIndexToName();
		
		final Map<String, Object> param = new HashMap<>();
		Set<Map.Entry<Integer, String>> entries = paramIndexToName.entrySet();
		for (Map.Entry<Integer, String> entry : entries) {
			param.put(entry.getValue(), args[entry.getKey()]);
		}
		return param;
	}
	
	
	/**
	 * 动态节点处理 ，检查 if 条件，动态整合sql 语句
	 */
	protected String dynamicNodeProcess(Map<String, Object> paramNameToValue, MixedSqlNode root) {
		DynamicContext dynamicContext = new DynamicContext(paramNameToValue);
		root.apply(dynamicContext);
		String dynamicSql = dynamicContext.getParseString();   // 里面还有  #{name} 这种东西
		
		return dynamicSql;
	}
	
	/**
	 * #{name}  替换为 ?, 并查询对应的实际参数值组装为 List
	 */
	protected List<Object> finallySqlProcess(StringBuilder buffer, String dynamicSql, Map<String, Object> paramNameToValue) {
		char[] chars = dynamicSql.toCharArray();
		List<Object> argList = new ArrayList<>(16);
		
		for (int index = 0; index < chars.length; index++) {
			char c = chars[index];
			if (c == '#' && index + 1 < chars.length && chars[index + 1] == '{') {
				index += 2;             // 找到了 #{ 开头部分
				int index2 = index;
				while (index2 < chars.length) {
					char c2 = chars[index2];
					if (c2 == '}') {
						break;
					}
					if (c2 == '#' || c2 == '{') {
						throw new RuntimeException("#{}表达式错误");
					}
					index2++;
				}
				if (chars[index2] != '}') {
					throw new RuntimeException("#{}表达式错误");
				}
				buffer.append(" ? ");
				String paramName = dynamicSql.substring(index, index2);
				Object value = MappingProcess.getElVal(paramNameToValue, paramName);
				argList.add(value);
				index = index2;
			} else {
				if (c == '#' || c == '{' || c == '}') {
					throw new RuntimeException("#{}表达式错误");
				}
				buffer.append(c);
			}
		}
		return argList;   // 最终得到的参数列表已经和 #{name} 顺序一致了，后面循环赋值就行
	}
	
	
	/**
	 * 分析行结果  , @NotField   @TableField 在这里实现功能
	 */
	protected List<?> parseResultSet(ResultSet rs, Class<?> clazz)
			throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, SQLException {
		rs.last();
		int row = rs.getRow();
		if (row == 0) {
			return new ArrayList<>();     // 没有数据的话立即返回一个空List
		}
		rs.beforeFirst();   // 移动回来
		
		
		// 简单的类型，不需要进 clazz 里面找字段，直接取值就行
		// clazz 不是bean 是单一类型时，不需要分析内部字段，直接取值就可以
		if (clazz.equals(int.class) || clazz.equals(Integer.class) || clazz.equals(long.class) || clazz.equals(Long.class) ||
				clazz.equals(float.class) || clazz.equals(Float.class) || clazz.equals(double.class) || clazz.equals(Double.class) ||
				clazz.equals(String.class)) {
			
			List<Object> results = new ArrayList<>(row);  // row 是结果行数
			TypeHandler<Object> typeHandler = register.getTypeHandler(clazz);
			while (rs.next()) {
				Object result = typeHandler.getResult(rs, 1);
				results.add(result);
			}
			return results;
		}
		
		
		// 复杂类型去分析字段
		
		// 提前分析一波字段和方法,免得每次循环生成一个对象都要再次去遍历一遍
		Field[] fields = clazz.getDeclaredFields();
		Method[] methods = new Method[fields.length];
		TypeHandler<?>[] handlers = new TypeHandler<?>[fields.length];
		String[] columnNames = new String[fields.length];
		int index = 0;
		for (Field field : fields) {
			if (field.isAnnotationPresent(NotField.class)) {    // 当前字段在数据库中没有，跳过
				continue;
			}
			// 成员字段属性名  映射到  数据库列标签名, 驼峰转下划线，再转全小写
			String propertyName = field.getName();
			String columnName = propertyName.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
			
			if (field.isAnnotationPresent(TableFiled.class)) {
				TableFiled tableField = field.getAnnotation(TableFiled.class);
				if (!tableField.exist()) {         // 当前字段在数据库中没有，跳过
					continue;
				}
				if (!tableField.value().isEmpty()) {
					columnName = tableField.value();   // 如果 注解字段不为空字符串，则按照注解属性使用
				}
			}
			
			Class<?> fieldType = field.getType();
			TypeHandler<?> typeHandler = register.getTypeHandler(fieldType);
			if (typeHandler == null) {
				continue;     // 应该抛异常么？  当前字段类型找不到对应的处理器，忽略掉继续下一个
			}
			
			// 查到属性对应的 setter 方法
			String setMethodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
			Method method = clazz.getMethod(setMethodName, fieldType);
			
			
			// 添加到列表中，准备后续使用
			methods[index] = method;
			handlers[index] = typeHandler;
			columnNames[index] = columnName;
			index++;
		}
		
		
		List<Object> results = new ArrayList<>(row);  // row 是结果行数
		while (rs.next()) {
			Object obj = clazz.getConstructor().newInstance();
			for (int i = 0; i < index; i++) {    // 不要按数组长度循环，不一定所有字段都赋值
				Object fieldValue = handlers[i].getResult(rs, columnNames[i]);
				methods[i].invoke(obj, fieldValue);
			}
			results.add(obj);
		}
		return results;
	}
	
	
	// 预处理SQL ,去掉动态节点和 动态参数#{} ，得到最终的  ? 形式sql
	protected String prepareSql(MapperStatement mapperStatement, Object[] args, List<Object> argList) {
		// 处理获得 参数名 -> 参数实际传入值 映射
		Map<String, Object> paramNameToValue = mappingParamNameToValue(mapperStatement, args);
		
		String finallySql = "";
		if (paramNameToValue == null) {
			// 为空，则视为固定sql 语句，直接执行 ，即没有任何  #{name} 能够解析
			finallySql = mapperStatement.getSql();
		} else {
			// 不为空，存在 #{} 这种东西 和 动态节点
			MixedSqlNode root = mapperStatement.getRoot();
			if (root != null) {
				// 动态节点，准备解析
				String dynamicSql = dynamicNodeProcess(paramNameToValue, root);
				
				StringBuilder finallySqlBuilder = new StringBuilder();
				argList.addAll(finallySqlProcess(finallySqlBuilder, dynamicSql, paramNameToValue));
				finallySql = finallySqlBuilder.toString();
			} else {
				// root 为null , 则没有动态节点，只是动态参数
				if (mapperStatement.getSql() == null) {
					throw new RuntimeException("sql 定义缺失");
				}
				StringBuilder finallySqlBuilder = new StringBuilder();
				argList.addAll(finallySqlProcess(finallySqlBuilder, mapperStatement.getSql(), paramNameToValue));
				finallySql = finallySqlBuilder.toString();
			}
		}
		
		// 最终的sql 格式化一下
		// String formatSql = finallySql.replaceAll("\\s{2,}", " ")
		// 					.replaceAll("=(?<=\\S)", " =")
		// 					.replaceAll("=(?=\\S)", "= ");
		/**
		 * 2022-01-20
		 * bug 修复,  格式化sql 会导致 parent_id != 0 这种语句变成  parent_id ! = 0  导致语法错误
		 */
		String formatSql = finallySql.replaceAll("\\s{2,}", " ")
									 .replaceAll("=(?=\\S)", "= ");
		
		
		log.info("准备执行sql: {}", formatSql);
		return formatSql;
	}
	
	// 遍历填充参数
	protected void setParameters(PreparedStatement ps, List<Object> argList) throws SQLException {
		if (argList != null) {
			for (int index = 0, size = argList.size(); index < size; index++) {
				Object arg = argList.get(index);
				if (arg == null) {
					//  参数可能为null ,为null 时 , arg.getClass() 会出现空指针异常，直接按 Object 统一处理掉
					TypeHandler<Object> typeHandler = register.getTypeHandler(Object.class);
					typeHandler.setParameter(ps, index + 1, null, null);
				} else {
					TypeHandler<Object> typeHandler = register.getTypeHandler(arg.getClass());
					typeHandler.setParameter(ps, index + 1, arg, null);
				}
				log.info("赋值参数: {}", arg);
			}
		}
	}
}
